
<script setup>
	
	import spellApi from '@/common/apis/spell';
	import wordWifi from './components/wifi.vue';
	import { imageBaseUrl } from '@/common/config';
	import popupCard from './components/popup-card.vue';
	import wordTimedown from './components/timedown.vue';
	import { ref, nextTick, onBeforeUnmount, computed } from 'vue';
	
	
	/** 导航栏 margin-right */
	const navRight = ref(0);
	
	// #ifdef MP-WEIXIN
	/** 获取小程序胶囊信息 */
	const { width } = uni.getMenuButtonBoundingClientRect()
	navRight.value = `${width}px`;
	// #endif
	
	/** 当前项索引 */
	const playIndex = ref(0);
	
	/** 词汇列表 */
	const vocabularylist = ref(uni.getStorageSync('_vocabulary-list'));
	
	/** 拼写单词 */
	const currentWord = ref(uni.getStorageSync('_current-word'));
	
	/** 拼写数据 */
	const spellData = uni.getStorageSync('_spell-data');
	
	/** 是否提示 */
	const isHint = ref(false);
	
	/** 是否播放单词 */
	const isPlayWord = ref(false);
	
	/** 播放结束 */
	const playEnd = () => {
		isPlayWord.value = false;
	};
	
	/** 监听自然结束 */
	uni.useAudio.onEnded(playEnd);
	
	/** 监听播放暂停 */
	uni.useAudio.onStop(playEnd);
	
	/** 播放单词 */
	const onPlayWord = () => {
		isPlayWord.value = true;
		uni.useAudio.src(currentWord.value?.voiceFile?.url);
	};
	
	
	/** 键盘信息 */
	const keyboardheightHeight = ref(0);
	
	/** 键盘弹起事件 */
	const onKeyboardheightchange = result => {
		const height = result?.height || result?.detail?.height;
		keyboardheightHeight.value = height - 20;
	};
	
	/** 监听键盘高度变化 */
	uni.onKeyboardHeightChange(onKeyboardheightchange);
	
	/** 组件卸载时 */
	onBeforeUnmount(() => {
		/** 移除监听键盘高度变化 */
		uni.offKeyboardHeightChange(onKeyboardheightchange);
	});
	
	
	/** 倒计时组件 */
	const wordTimedownRef = ref({});
	
	/** 拼写值 */
	const spellValue = ref('');
	
	/** 提交函数 */
	const _submit = async () => {
		await spellApi.saveWords({ words: [{ id: currentWord.value.id, isSpell: 1 }], logId: spellData.logId });
		uni.setStorageSync('-spell-time', wordTimedownRef.value?.times);
		uni.navigateBack();
	};
	
	
	/** 拼写结果 */
	const spellResult = ref();
	
	/** shake */
	const shake = ref(false);
	
	/** 记录 */
	const record = ref([]);
	
	/** create span */
	const createSpan = (str, color, cprrect = color === '#00c386') => {
		return {
			str,
			color,
			cprrect,
			value: function() {
				return `<span style="color: ${this.color};${str === '_' ? 'padding-right: 2px' : ''}">${this.str}</span>`;
			}
		}
	};
	
	/** 拼写常量 */
	const SPELL_COLOR = {
		/** 不在原单词范围内 */
		NO: '#8e888b',
		
		/** 错误 */
		ERROR: '#e16857',
		
		/** 正确 */
		CORRECT: '#00c386'
	};
	
	/** 拼写转标签 */
	const spellTransferTag = () => {
		/** 拼写的每一项 */
		record.value = [];
		
		/**  原单词*/
		const word = currentWord.value.word;
		
		/** 循环拼写 */
		for (let index = 0, wordIndex = 0, errorIndex = 0; index < spellValue.value.length; index++) {
			/** 拼写的每一项 */
			const spellItem = spellValue.value[index];
			
			if (word === spellValue.value) {
				/** 拼写全等 */
				record.value.push(createSpan(spellItem, SPELL_COLOR.CORRECT));
			
			} else {
				/** 拼写不全等 */
				if (spellItem === word[wordIndex]) {
					/** 正确 */
					record.value.push(createSpan(spellItem, SPELL_COLOR.CORRECT));
					wordIndex++;
					errorIndex++;
					
				} else if (record.value.length >= word.length) {
					/** 超出原单词长度 */
					record.value.push(createSpan(spellItem, SPELL_COLOR.NO));
					
				} else {
					/** 错误 */
					const returnSliceStartIndex = () => {
						let _index = word.indexOf(spellItem) + 1;
						let _word = word;
						const frequency = record.value.filter(item => item.str === spellItem).length;
						const _returnSliceStartIndex = () => {
							if (frequency <= 1) return;
							for (let _i = 0; _i <= frequency; _i++) {
								_word = _word.slice(_index);
								_index += _word.indexOf(spellItem) + 1;
							};
						};
						
						_returnSliceStartIndex();
						return frequency ? _index : errorIndex;
					};
					
					const lastWord = word.slice(returnSliceStartIndex());
					if (lastWord.includes(spellItem) && record.value.length + 1 < word.length) {
						/** 用下划线补齐单词中间的部分 */
						const underline = Array.from({ length: lastWord.indexOf(spellItem) }).map(() => createSpan('_', SPELL_COLOR.ERROR));
						underline.length && record.value.push(...underline);
						errorIndex++;
					};
					
					record.value.push(createSpan(spellItem, SPELL_COLOR[lastWord.includes(spellItem) ? 'CORRECT' : 'ERROR']));
				};
			};
		};
		
		
		if (spellValue.value.length < word.length) {
			/** 拼写的长度小于原单词的长度时 */
			record.value[record.value.find(item => item.cprrect) ? 'push' : 'unshift'](...Array.from({ length: word.length - record.value.length }).map(() => createSpan('_', SPELL_COLOR.ERROR)));
		};
		
		/** 加入到 template */
		const correctCount = record.value.filter(item => item.cprrect).length;
		spellResult.value = record.value.map((item, index) => {
			item.color = correctCount === word.length && item.color === '#e16857' ? SPELL_COLOR.NO : item.color;
			return item.value();
			
		}).join('');
	};
	
	/** 提交 */
	const onSubmit = async () => {
		isHint.value = true;
		if (!spellValue.value) return;
		spellTransferTag();
		
		if (spellValue.value !== currentWord.value.word) {
			shake.value = true;
			setTimeout(() => shake.value = false, 800)
			return;
		};
		
		_submit();
	};
	
	/** 倒计时结束 */
	const onTimedown = () => {
		uni.$fui.modal({
			title: '提示',
			success: _submit,
			showCancel: false,
			content: '本单词拼写时间已到，请提交拼写'
		});
	};
	
	/** 是否显示答题卡 */
	const showPopupCard = ref(false);
	
	/** input focus */
	const inputFocus = ref(true);
	
	/** 打开复习卡 */
	const openCard = () => {
		inputFocus.value = false;
		showPopupCard.value = true;
	};
	
	/** 重置 */
	const onReset = () => {
		// spellValue.value = '';
		// spellResult.value = '';
	};
	
	/** 输入时 */
	const onInput = ({ detail }) => {
		if (spellResult.value) {
			isHint.value = false;
			spellResult.value = '';
			spellValue.value = record.value.filter(item => item.str !== '_').map(item => item.str).join('');
		};
	};
	
	/** cursor-color */
	const cursorColor = (() => {
		// #ifndef MP-WEIXIN
		return '#46c69e';
		// #endif
		
		// #ifdef MP-WEIXIN
		return uni.getSystemInfoSync().platform === 'ios' ? '#46c69e' : 'green';
		// #endif
		
	})();
	
	/** 失去焦点时 */
	const onBlur = () => {
		if (showPopupCard.value) return;
		inputFocus.value = true;
	};
	
	/** 缓存计时 */
	const onInterval = time => {
		uni.setStorageSync('_interval', time);
	};
	
	
	/** 自定义键盘 */
	const isUpper = ref(false);
	const safeAreaInsets = uni.getSystemInfoSync().safeAreaInsets.bottom * 2 + 100;
	const letterToUpperOrLower = letter => letter[isUpper.value ? 'toLocaleUpperCase' : 'toLocaleLowerCase']();
	const keyboardFirstline = computed(() => ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p'].map(letterToUpperOrLower));
	const keyboardSecondline = computed(() => ['a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l'].map(letterToUpperOrLower));
	const keyboardThreeline = computed(() => ['z', 'x', 'c', 'v', 'b', 'n', 'm'].map(letterToUpperOrLower));
	const onletter = letter => {
		spellValue.value += letter;
	};
	const ondeleteletter = () => {
		if (spellResult.value) {
			isHint.value = false;
			spellResult.value = '';
			spellValue.value = record.value.map(item => item.str).join('');
		};
		
		if (!spellValue.value.length) return;
		spellValue.value = spellValue.value.slice(0, -1);
	};
	
	
</script>

<template>
	<view class="containerbox">
		<uv-navbar auto-back placeholder bg-color="transparent">
			<template #right>
				<view :style="{ marginRight: navRight }" class="flex">
					<view class="flex flex-column justify-center items-center mr-15">
						<image :src="`${$imageBaseUrl}time.png`" class="h-25" style="width: 58rpx" />
						<word-timedown size="18" :flash="false" @time-fun="onTimedown" ref="wordTimedownRef" @onInterval="onInterval" />
					</view>
					
					<view class="w-50 mr-10">
						<uv-image :src="`${$imageBaseUrl}card.png`" width="50rpx" height="50rpx" @tap="openCard" />
					</view>
				</view>
			</template>
		</uv-navbar>
		
		
		<view class="text-center w-full mt-300 bold text-80" :class="{ shake }">
			<!-- <input
				type="text" 
				confirm-hold
				hold-keyboard
				@blur="onBlur"
				inputmode="none"
				@input="onInput"
				input-mode="text"
				@confirm="onSubmit"
				:focus="inputFocus" 
				v-model="spellValue"
				:cursor-color="cursorColor" 
				class="inout-view overflow-hidden text-80"
				@keyboardheightchange="onKeyboardheightchange" 
				:style="{ height: spellResult ? '0' : '108rpx', minHeight: '0' }"
			/> -->
			<view class="rich-text-view h-108 text-80">
				<rich-text :nodes="spellResult ? spellResult : spellValue" />
				<view class="rich-text-view-cursor-blinks" />
			</view>
 		</view>
		
		
		<view class="text-center bold mt-50">
			<view class="text-80" v-if="isHint">{{ currentWord.word }}</view>
			<view v-else class="text-26">{{ currentWord.translation }}</view>
		</view>
		
		
		<view class="flex items-center justify-between px-50 mt-auto pb-30 text-48">
			<view 
				@tap="isHint = !isHint" 
				hover-class="botton-active"
				:class="{ selected: isHint }"
				class="spell spell-tishi w-90 h-90 bgc-white round-30 flex items-center justify-center transition-bg" 
			/>
			
			<view @tap="onPlayWord" class="relative w-90 h-90 bgc-white round-30 flex items-center justify-center text-30 transition-bg" hover-class="botton-active" v-if="!isPlayWord">
				<view class="absolute" style="left: 40rpx; top: 20rpx; transform: scale(1.2)">
					<word-wifi color="#00c386" />
				</view>
			</view>
			
			<text class="px-30 h-90 bgc-white round-30 flex items-center justify-center text-30 normal text-9ca2a0" v-else>{{ currentWord.soundmark }}</text>
			
			<view class="spell spell-zhengque w-90 h-90 bgc-white round-30 flex items-center justify-center transition-bg" hover-class="botton-active" @tap="onSubmit" />
		</view>
		
		<!-- <view :style="{ height: `${keyboardheightHeight}px` }" class="transition-height" /> -->
		
		
		<!-- 自定义键盘 -->
		<view style="background-color:#d0d3da;padding-top:20rpx">
			<!-- 第一行 -->
			<view style="display:flex;align-items:center;justify-content:space-between;padding:0 7rpx">
				<view 
					:key="letter"
					@tap="onletter(letter)"
					v-for="letter in keyboardFirstline"
					style="width:70rpx;margin:0 7rpx;background-color:white;border-radius:12rpx;display:flex;align-items:center;justify-content:center;height:80rpx;font-size:36rpx;box-shadow:0 5rpx 1px #abaeb3"
				>
					{{ letter }}
				</view>
			</view>
			
			<!-- 第二行 -->
			<view style="display:flex;align-items:center;justify-content:space-between;padding:0 35rpx;margin-top: 20rpx">
				<view 
					:key="letter"
					@tap="onletter(letter)"
					v-for="letter in keyboardSecondline"
					style="width:70rpx;margin:0 7rpx;background-color:white;border-radius:12rpx;display:flex;align-items:center;justify-content:center;height:80rpx;font-size:36rpx;box-shadow:0 5rpx 1px #abaeb3"
				>
					{{ letter }}
				</view>
			</view>
			
			<!-- 第三行 -->
			<view style="display:flex;align-items:center;justify-content:space-between;padding:0 15rpx;margin-top: 20rpx">
				<view 
					@tap="isUpper = !isUpper"
					style="background-color: #bec0cc;border-radius:12rpx;display:flex;align-items:center;justify-content:center;width: 75rpx;margin-right: 27rpx;height:80rpx;box-shadow:0 5rpx 1px #abaeb3"
				>
					<image :src="`${imageBaseUrl}/keyboard/${isUpper ? 'upper' : 'lower'}.png`" style="width:40rpx" mode="widthFix" />
				</view>
				
				<view 
					:key="letter"
					@tap="onletter(letter)"
					v-for="letter in keyboardThreeline"
					style="width:70rpx;margin:0 7rpx;background-color:white;border-radius:12rpx;display:flex;align-items:center;justify-content:center;height:80rpx;font-size:36rpx;box-shadow:0 5rpx 1px #abaeb3"
				>
					{{ letter }}
				</view>
				
				<view 
					@tap="ondeleteletter"
					style="background-color: #bec0cc;border-radius:12rpx;display:flex;align-items:center;justify-content:center;width: 75rpx;margin-left: 27rpx;height:80rpx;box-shadow:0 5rpx 1px #abaeb3"
				>
					<image :src="`${imageBaseUrl}/keyboard/delete.png`" style="width:40rpx" mode="widthFix" />
				</view>
			</view>
			
			<view :style="{ height: `${safeAreaInsets}rpx` }" />
		</view>
		
		<popup-card :list="vocabularylist" v-model="showPopupCard" :current="vocabularylist.findIndex(item => item.id === currentWord.id)" :button="false" @close="inputFocus = true" />
	</view>
</template>


<style>
	page {
		background: linear-gradient(165deg, #c4f2e7, #f5f8e5, #f1f1f1, #f1f1f1);
	}
</style>

<style scoped lang="scss">
	.top-0{top: 0}
	.h-108{height: 108rpx}
	.overflow-hidden{overflow: hidden}
	.inout-view::placeholder{font-weight: normal;font-size: 50}
	.mt-300{margin-top: 300rpx}
	.mt-auto{margin-top: auto}
	.opacity-70{opacity: .7;transition:  opacity .3s}
	.spell{transition: color .3s}
	.selected{color: #00c386}
	.normal{font-weight: normal}
	.text-9ca2a0{color: #9ca2a0}
	.mt-6{margin-top: 6rpx}
	.transition-bg{transition: background-color .3s}
	.botton-active{background-color: #f0f0ee}
	.transition-height{transition: height .25s}
	.rich-text-view{position: relative;width: fit-content;margin: 0 auto}
	.rich-text-view-cursor-blinks{position: absolute;right: -5rpx;top: 0;width: 5rpx;height: 100%;background-color: #00c386; animation: cursor-blinks 800ms infinite steps(1, start)}
	.shake{animation: shake 800ms ease-in-out;transform-style: preserve-3d}
	
	 @keyframes shake{
		10%,90%{transform: translate3d(-1px, 0, 0)}
		20%,80%{transform: translate3d(+2px, 0, 0)}
		30%,70%{transform: translate3d(-4px, 0, 0)}
		40%,60%{transform: translate3d(+4px, 0, 0)}
		50%{transform: translate3d(-4px, 0, 0)}
	}
	@keyframes cursor-blinks {
		0%{opacity: 1;display: block}
		50%{opacity: 0;display: none}
		100%{opacity: 1;display: block}
	}
</style>